#include "hr_tbr.hpp"
#ifndef __SYNTHESIS__
#include <iostream>
#include <algorithm> // For std::min/max in C-simulation
#include <iomanip>  // For std::fixed, std::setprecision
#endif

// 辅助函数：更新Bounding Box (像素坐标)
void update_bounding_box_pixels(
    fpal::fp_coord_t vx, fpal::fp_coord_t vy,
    fpal::fp_screen_pixel_idx_t& min_x_px, fpal::fp_screen_pixel_idx_t& max_x_px,
    fpal::fp_screen_pixel_idx_t& min_y_px, fpal::fp_screen_pixel_idx_t& max_y_px
) {
    #pragma HLS INLINE
    fpal::fp_screen_pixel_idx_t cur_x_idx = fpal::to_fp_screen_pixel_idx(vx);
    fpal::fp_screen_pixel_idx_t cur_y_idx = fpal::to_fp_screen_pixel_idx(vy);
    
    // Using simple if conditions instead of std::min/max for HLS compatibility
    if (cur_x_idx < min_x_px) min_x_px = cur_x_idx;
    if (cur_x_idx > max_x_px) max_x_px = cur_x_idx;
    if (cur_y_idx < min_y_px) min_y_px = cur_y_idx;
    if (cur_y_idx > max_y_px) max_y_px = cur_y_idx;
}


// ===========================================================
// HR_TBR_Binning 模块实现 (Triangle Binning)
// ===========================================================
void hr_tbr_binning_with_bram_write(
    hls::stream<raster_params_packet_fixed>& input_raster_params_stream,
    // all_triangles_bram 更改为输出 stream
    hls::stream<binned_triangle_packet_fixed>& s_binned_triangles_out, 
    hls::stream<fpal::fp_screen_pixel_idx_t>& s_max_x_res_in,
    hls::stream<fpal::fp_screen_pixel_idx_t>& s_max_y_res_in,
    hls::stream<ap_uint<16>>& s_num_triangles_to_process_in,
    hls::stream<TileListDataPacket>& s_tile_list_out,
    hls::stream<fpal::fp_screen_pixel_idx_t>& s_max_x_res_out,
    hls::stream<fpal::fp_screen_pixel_idx_t>& s_max_y_res_out,
    hls::stream<ap_uint<16>>& s_num_triangles_to_process_out // 输出流
) {
    #pragma HLS INLINE OFF // 这是一个 DATAFLOW 阶段，其自身不应被内联

    // 在此函数内部声明并绑定这些 BRAM
    static ap_uint<TILE_LIST_MAX_TRIANGLES_PER_TILE_LOG2> tile_list_count[MAX_NUM_TILES_Y][MAX_NUM_TILES_X];
    #pragma HLS bind_storage variable=tile_list_count type=ram_t2p impl=bram

    // TRIANGLE_ID_BITS 的位宽已在 hr_tbr.hpp 中更新
    static ap_uint<TRIANGLE_ID_BITS> tile_list_data[MAX_NUM_TILES_Y][MAX_NUM_TILES_X][TILE_LIST_MAX_TRIANGLES_PER_TILE];
    #pragma HLS bind_storage variable=tile_list_data type=ram_t2p impl=bram

    // --- 接收流式参数 ---
    const ap_uint<16> num_triangles_to_process = s_num_triangles_to_process_in.read();
    const fpal::fp_screen_pixel_idx_t max_x_res = s_max_x_res_in.read();
    const fpal::fp_screen_pixel_idx_t max_y_res = s_max_y_res_in.read();

    // --- 转发流式参数给下一个模块 ---
    s_max_x_res_out.write(max_x_res);
    s_max_y_res_out.write(max_y_res);
    s_num_triangles_to_process_out.write(num_triangles_to_process); // 转发

    // --- 在 DATAFLOW 进程内部初始化 Tile List count ---
    // 这将确保所有对 tile_list_count 的写入都由这个进程控制。
    ap_uint<8> num_tiles_x_actual = (max_x_res + TILE_WIDTH - 1) / TILE_WIDTH;
    ap_uint<8> num_tiles_y_actual = (max_y_res + TILE_HEIGHT - 1) / TILE_HEIGHT;

    TILE_LIST_INIT_Y:
    for (ap_uint<8> ty = 0; ty < num_tiles_y_actual; ++ty) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y
        TILE_LIST_INIT_X:
        for (ap_uint<8> tx = 0; tx < num_tiles_x_actual; ++tx) {
            #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X
            #pragma HLS PIPELINE II=1
            tile_list_count[ty][tx] = 0;
        }
    }
    // -------------------------------------------------------------


    TRIANGLE_BINNING_LOOP:
    // LOOP_TRIPCOUNT min=1 max=MAX_NUM_TRIANGLES 将根据 hr_tbr.hpp 中的新定义 (511) 自动更新
    for (ap_uint<16> triangle_idx = 0; triangle_idx < num_triangles_to_process; ++triangle_idx) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TRIANGLES 
        
        raster_params_packet_fixed current_triangle_params;
        input_raster_params_stream.read(current_triangle_params);

        fpal::fp_coord_t v0_x_fx = current_triangle_params.v0_x;
        fpal::fp_coord_t v0_y_fx = current_triangle_params.v0_y;
        fpal::fp_coord_t v1_x_fx = current_triangle_params.v1_x;
        fpal::fp_coord_t v1_y_fx = current_triangle_params.v1_y;
        fpal::fp_coord_t v2_x_fx = current_triangle_params.v2_x;
        fpal::fp_coord_t v2_y_fx = current_triangle_params.v2_y;

        // Calculate Pixel-level Bounding Box
        fpal::fp_screen_pixel_idx_t min_x_px_raw = fpal::to_fp_screen_pixel_idx(v0_x_fx); // 获取原始转换结果
        fpal::fp_screen_pixel_idx_t min_y_px_raw = fpal::to_fp_screen_pixel_idx(v0_y_fx); // 获取原始转换结果
        
        fpal::fp_screen_pixel_idx_t min_x_px = min_x_px_raw; // 初始化
        fpal::fp_screen_pixel_idx_t max_x_px = min_x_px;
        fpal::fp_screen_pixel_idx_t min_y_px = min_y_px_raw;
        fpal::fp_screen_pixel_idx_t max_y_px = min_y_px;

        update_bounding_box_pixels(v1_x_fx, v1_y_fx, min_x_px, max_x_px, min_y_px, max_y_px);
        update_bounding_box_pixels(v2_x_fx, v2_y_fx, min_x_px, max_x_px, min_y_px, max_y_px);

        fpal::fp_screen_pixel_idx_t screen_max_x_idx = max_x_res - 1;
        fpal::fp_screen_pixel_idx_t screen_max_y_idx = max_y_res - 1;

        // Using simple if conditions for clamp instead of std::min/max for HLS compatibility
        if (min_x_px < 0) min_x_px = 0;
        if (min_x_px > screen_max_x_idx) min_x_px = screen_max_x_idx;
        if (max_x_px < 0) max_x_px = 0;
        if (max_x_px > screen_max_x_idx) max_x_px = screen_max_x_idx;

        if (min_y_px < 0) min_y_px = 0;
        if (min_y_px > screen_max_y_idx) min_y_px = screen_max_y_idx;
        if (max_y_px < 0) max_y_px = 0;
        if (max_y_px > screen_max_y_idx) max_y_px = screen_max_y_idx;

        if (min_x_px > max_x_px) max_x_px = min_x_px;
        if (min_y_px > max_y_px) max_y_px = min_y_px;

        // Calculate Tile-level Bounding Box
        ap_uint<8> min_tile_x = min_x_px / TILE_WIDTH;
        ap_uint<8> max_tile_x = max_x_px / TILE_WIDTH;
        ap_uint<8> min_tile_y = min_y_px / TILE_HEIGHT;
        ap_uint<8> max_tile_y = max_y_px / TILE_HEIGHT;

        ap_uint<8> max_possible_tile_x_idx = num_tiles_x_actual - 1;
        ap_uint<8> max_possible_tile_y_idx = num_tiles_y_actual - 1;

        // Clamping tile coordinates
        if (min_tile_x < 0) min_tile_x = 0;
        if (min_tile_x > max_possible_tile_x_idx) min_tile_x = max_possible_tile_x_idx;
        if (max_tile_x < 0) max_tile_x = 0;
        if (max_tile_x > max_possible_tile_x_idx) max_tile_x = max_possible_tile_x_idx;

        if (min_tile_y < 0) min_tile_y = 0;
        if (min_tile_y > max_possible_tile_y_idx) min_tile_y = max_possible_tile_y_idx;
        if (max_tile_y < 0) max_tile_y = 0;
        if (max_tile_y > max_possible_tile_y_idx) max_tile_y = max_possible_tile_y_idx;

        // Populate the binned_triangle_packet_fixed structure
        binned_triangle_packet_fixed binned_packet;
        binned_packet.v0_x_ref = v0_x_fx;
        binned_packet.v0_y_ref = v0_y_fx;
        binned_packet.v0_z_ref = current_triangle_params.v0_z;
        binned_packet.edge0_A = current_triangle_params.edge0_A; binned_packet.edge0_B = current_triangle_params.edge0_B; binned_packet.edge0_C = current_triangle_params.edge0_C;
        binned_packet.edge1_A = current_triangle_params.edge1_A; binned_packet.edge1_B = current_triangle_params.edge1_B; binned_packet.edge1_C = current_triangle_params.edge1_C;
        binned_packet.edge2_A = current_triangle_params.edge2_A; binned_packet.edge2_B = current_triangle_params.edge2_B; binned_packet.edge2_C = current_triangle_params.edge2_C;
        binned_packet.dzdx = current_triangle_params.dzdx;
        binned_packet.dzdy = current_triangle_params.dzdy;
        
        // 打包颜色分量
        binned_packet.packed_pure_color = ( (ap_uint<24>)current_triangle_params.pure_color_R << 16) |
                                          ( (ap_uint<24>)current_triangle_params.pure_color_G << 8)  |
                                          ( (ap_uint<24>)current_triangle_params.pure_color_B << 0);
        // 已移除冗余的 pure_color_R/G/B 成员赋值。

        binned_packet.min_x_px = min_x_px; binned_packet.max_x_px = max_x_px;
        binned_packet.min_y_px = min_y_px; binned_packet.max_y_px = max_y_px;
        binned_packet.min_tile_x = min_tile_x; binned_packet.max_tile_x = max_tile_x;
        binned_packet.min_tile_y = min_tile_y; binned_packet.max_tile_y = max_tile_y;
        
        binned_packet.is_valid_triangle = (min_x_px <= max_x_px && min_y_px <= max_y_px);

        // 将打包的三角形信息写入 Stream
        s_binned_triangles_out.write(binned_packet);

        // Update the Tile List Buffer if the triangle is valid
        if (binned_packet.is_valid_triangle) {
            TILE_BINNING_X_LOOP:
            for (ap_uint<8> tile_x = min_tile_x; tile_x <= max_tile_x; ++tile_x) {
                #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X
                TILE_BINNING_Y_LOOP:
                for (ap_uint<8> tile_y = min_tile_y; tile_y <= max_tile_y; ++tile_y) {
                    #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y

                    ap_uint<TILE_LIST_MAX_TRIANGLES_PER_TILE_LOG2> current_count = tile_list_count[tile_y][tile_x];
                    
                    // Add triangle ID if space is available
                    if (current_count < TILE_LIST_MAX_TRIANGLES_PER_TILE) {
                        // triangle_idx 的位宽 (ap_uint<16>) 足够，这里会自动截断为 TRIANGLE_ID_BITS (9)
                        tile_list_data[tile_y][tile_x][current_count] = triangle_idx; // <-- 关键写入点
                        tile_list_count[tile_y][tile_x] = current_count + 1;
                    }
                }
            }
        }
    } // end TRIANGLE_BINNING_LOOP

    // 在 TRIANGLE_BINNING_LOOP 结束后，将 BRAM 内容流化出去 (此处是将 tile_list_data 流化出去)
    FORWARD_TILE_LIST_Y_LOOP:
    for (ap_uint<8> ty = 0; ty < num_tiles_y_actual; ++ty) {
        #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_Y
        FORWARD_TILE_LIST_X_LOOP:
        for (ap_uint<8> tx = 0; tx < num_tiles_x_actual; ++tx) {
            #pragma HLS LOOP_TRIPCOUNT min=1 max=MAX_NUM_TILES_X
            #pragma HLS PIPELINE II=1

            TileListDataPacket packet;
            packet.tile_x = tx;
            packet.tile_y = ty;
            packet.count = tile_list_count[ty][tx];

            // 拷贝 tile_list_data
            COPY_TRI_IDS_TO_STREAM:
            for (int k = 0; k < TILE_LIST_MAX_TRIANGLES_PER_TILE; ++k) {
                // 对于小循环，UNROLL 通常性能更好，特别是当数据可以完全并行读取时
                #pragma HLS UNROLL 
                packet.data[k] = tile_list_data[ty][tx][k];
            }
            s_tile_list_out.write(packet);
        }
    }
}

